/*
 ============================================================================
 Name        : tpRaid.c
 Author      : superacidos
 Version     :
 Copyright   : Your copyright notice
 Description : Hello World in C, Ansi-style
 ============================================================================
 */

	#include "config.h"
	#include "ppdHandler.h"
	#include <netinet/in.h>
	#include <sys/types.h>
	#include <sys/socket.h>
	#include <arpa/inet.h>

	int32_t main(void)
    {
        fd_set master;   // conjunto maestro de descriptores de fichero
        fd_set read_fds; // conjunto temporal de descriptores de fichero para select()
        struct sockaddr_in myaddr;     // dirección del servidor
        struct sockaddr_in remoteaddr; // dirección del cliente
        int32_t fdmax;        // número máximo de descriptores de fichero
        int32_t listener;     // descriptor de socket a la escucha
        int32_t newfd;        // descriptor de socket de nueva conexión aceptada
        int32_t nbytes;
        int32_t yes=1;        // para setsockopt() SO_REUSEADDR, más abajo
        int32_t addrlen;
        int32_t i,pid;
        struct timeval timeout;
        timeout.tv_sec = 0;
        timeout.tv_usec = 1000;

        FD_ZERO(&master);    // borra los conjuntos maestro y temporal
        FD_ZERO(&read_fds);

        stPedido *pedido;
        char *dirIp = (char *)malloc(16);
        char *puerto = (char *)malloc(10);
        char *hayQueLoguear= (char *)malloc(5);

        loadConfig(dirIp,puerto,hayQueLoguear);

        int32_t intHayQueLoguear = atoi(hayQueLoguear);
        free(hayQueLoguear);
        // obtener socket a la escucha
        if ((listener = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
            perror("socket");
            exit(1);
        }
        pid =getpid();
        // obviar el mensaje "address already in use" (la dirección ya se está usando)
        if (setsockopt(listener, SOL_SOCKET, SO_REUSEADDR, &yes,
                                                            sizeof(int)) == -1) {
            perror("setsockopt");
            exit(1);
        }

        // enlazar
        myaddr.sin_family = AF_INET;
        myaddr.sin_addr.s_addr = inet_addr(dirIp);
        myaddr.sin_port = htons(atoi(puerto));
        memset(&(myaddr.sin_zero), '\0', 8);
        if (bind(listener, (struct sockaddr *)&myaddr, sizeof(myaddr)) == -1) {
            perror("bind");
            exit(1);
        }

        // escuchar
        if (listen(listener, 10) == -1) {
            perror("listen");
            exit(1);
        }

        // añadir listener al conjunto maestro
        FD_SET(listener, &master);

        // seguir la pista del descriptor de fichero mayor
        fdmax = listener; // por ahora es éste

        PAQUETE_NIPC_CABECERA *cabecera;
        PAQUETE_NIPC *paquete;

        printf("se encendio el proceso RAID 1 en la ip: %s puerto: %s \n",dirIp,puerto);

        pthread_mutex_t semaforo;


        // bucle principal
        for(;;) {
            read_fds = master; // cópialo
            if (select(fdmax+1, &read_fds, NULL, NULL, NULL) == -1) {
                perror("select");
                exit(1);
            }

            // explorar conexiones existentes en busca de datos que leer
            for(i = 0; i <= fdmax; i++) {
                if (FD_ISSET(i, &read_fds)) { // ¡¡tenemos datos!!
                    if (i == listener) {
                        // gestionar nuevas conexiones
                        addrlen = sizeof(remoteaddr);
                        if ((newfd = accept(listener, (struct sockaddr *)&remoteaddr,&addrlen)) == -1) {
                            perror("accept");
                        } else {
                            FD_SET(newfd, &master); // añadir al conjunto maestro
                            if (newfd > fdmax) {    // actualizar el máximo
                                fdmax = newfd;
                            }
                            printf("selectserver: new connection from %s on "
                                "socket %d \n", inet_ntoa(remoteaddr.sin_addr), newfd);
                        }
                    } else {
                    	cabecera = (PAQUETE_NIPC_CABECERA*)malloc(sizeof(PAQUETE_NIPC_CABECERA));
                        // gestionar datos de un cliente
                        if ((nbytes = recv(i, cabecera, sizeof(PAQUETE_NIPC_CABECERA), MSG_WAITALL)) <= 0) {
                            // error o conexión cerrada por el cliente

                            if (nbytes == 0) {
                                // conexión cerrada
                                printf("selectserver: socket %d hung up \n", i);
                                //SE CERRO LA CONEXION CON EL SOCKET VER SI
                                //BUSCAMOS POR SOCKET EL DISCO Y DECIMOS QUE ESTA CAIDO
                                //ES ACA CUANDO TENEMOS QUE REDISTRIBUIR LAS LECTURAS QUE TIENE ASIGNADAS
                                //ENTRE EL RESTO DE LOS PPD


                                //pthread_mutex_lock(&semaforo);
                                //Redistribuir(i);
                                //pthread_mutex_unlock(&semaforo);


                                logMessage("Desconexion de un disco",pid,0,1,"se desconecto un disco",intHayQueLoguear);


                            } else {
                                //perror("recv");
                            	printf("error recv socket %d\n",i);
                            	//pthread_mutex_lock(&semaforo);
                            	//Redistribuir(i);
                            	//pthread_mutex_unlock(&semaforo);
                            	logMessage("Desconexion de un disco",pid,0,1,"se desconecto un disco",intHayQueLoguear);

                            }
                            close(i); // ¡Hasta luego!
                            FD_CLR(i, &master); // eliminar del conjunto maestro

                        } else {
                            // tenemos datos de algún cliente
                            //ACA VA NUESTRA LOGICA EN BASE A LO QUE RECIBAMOS DEL PROTOCOLO NIPC

                        	switch(cabecera->type)
                        	{
                        		case (uint8_t)HANDSHAKE:
                        			if(cabecera->payloadlength == 0)//PFS
                        			{
                        				char *respuesta;
										if (hayUnDiscoConectado() > -1)
										{
											respuesta =serializarNipcSinPayload(PAYOK,0);
											send(i,respuesta,sizeof(PAQUETE_NIPC_CABECERA),0);
											free(respuesta);
										}
										else
										{
											char * error = (char*)malloc(50);

											strcpy(error,"no hay discos conectados");
											strcat(error,"\0");
											int32_t payloadlength = strlen(error) +1;
											respuesta = serializarNipc(PAYFALLO,error,payloadlength);
											send(i,respuesta,sizeof(PAQUETE_NIPC_CABECERA) + payloadlength,0);

											//free(respuesta);
											free(error);

										}
                        			}
                        			else //PPD
                        			{

                        				if (hayUnDiscoConectado() == -1)
										{
                        					pthread_mutex_init(&semaforo,NULL);
                        					int32_t pid = getpid();
											logMessage("Cambio de estado RAID",pid,0,0,"ACTIVO",intHayQueLoguear);

										}

                        				paquete = (PAQUETE_NIPC*)malloc(sizeof(PAQUETE_NIPC_CABECERA) + cabecera->payloadlength);
										paquete->payload = (char *)malloc(cabecera->payloadlength);
										paquete->cabecera = cabecera;

										nbytes = recv(i, paquete->payload, cabecera->payloadlength, MSG_WAITALL);
										printf("recibimos el disco: %s \n",paquete->payload);
										FD_CLR(i, &master);
										if (AgregarDisco(paquete,i,dirIp,puerto,&semaforo,intHayQueLoguear) == 0)
										{

											close(i); // ¡Hasta luego!
											//FD_CLR(i, &master); // eliminar del conjunto maestro
										}
										else
										{
											logMessage("Conexion de un disco",pid,0,0,"Se conecto un disco",intHayQueLoguear);
										}


										free(paquete->payload);
										free(cabecera);
										free(paquete);
                        			}
                        			break;
                        		case LEER:
                        			pedido = (stPedido *)malloc(sizeof(stPedido));

									if ((nbytes = recv(i, pedido, sizeof(stPedido), MSG_WAITALL)) > 0)
									{
										pedido->socketPFS = i;
										//pthread_mutex_lock(&semaforo);
										AgregarLectura(pedido,&semaforo);
										//pthread_mutex_unlock(&semaforo);
									}
									else
									{
										//error ver descripciones
									}
									free(cabecera);
                        			break;
                        		case ESCRIBIR:
                        			pedido = (stPedido *)malloc(sizeof(stPedido));

									if ((nbytes = recv(i, pedido, sizeof(stPedido), MSG_WAITALL)) > 0)
									{
										pedido->socketPFS = i;
										pthread_mutex_lock(&semaforo);
										AgregarEscritura(pedido);
										pthread_mutex_unlock(&semaforo);
									}
									else
									{
										//error ver descripciones
									}
									free(cabecera);
									break;

                        		default:
                        			printf("HOY NO\n");
                        			break;

                        	}

                        	/*printf("%s \n",buf);
                        	for(j = 0; j <= fdmax; j++) {
                                // ¡enviar a todos los clientes conectados!
                                if (FD_ISSET(j, &master)) {
                                    // excepto al listener y a nosotros mismos
                                    if (j != listener && j != i) {
                                        if (send(j, buf, nbytes, 0) == -1) {
                                            perror("send");
                                        }
                                    }
                                }
                            }*/
                        }
                    } // Esto es ¡TAN FEO!
                }
            }
        }

        return 0;
    }



